import os

import numpy as np

from core.object import BarData, MessageData
from core.template import Template
from core.constant import *
from conf import conf
from tools.file_manager import gen_data_name
from module.logic_module.exchange_server.trade_server import TradeServer
from module.logic_module.exchange_server.data_server import DataServer
import time


class Engine:
    def __init__(self, super_model):
        self.super_model = super_model
        # 根据不同的模式加载不同的交易模块
        self.trade_server: TradeServer = None  # 回测、仿真、实盘模式
        self.data_server: DataServer = None  # 实盘数据、历史数据模式
        self.template: Template = None  # 指向策略
        self.is_end = None  # 标记实时模式下是否结束

        # 初始视图类变量数据及其实体类变量
        self.fighting_mode_ls = []

    def add_data_server(self, data_server):
        self.data_server = data_server(self)

    def add_trade_server(self, trade_server):
        self.trade_server = trade_server(self)

    def add_template(self, template: Template):
        # 加载策略
        self.template = template
        # 关联engine实例
        self.template.set_engine(self)

    def loop(self, data_path, is_email, fighting_mode_name: FightingMode):
        self.monitor(data_path, is_traverse=True)
        self.set_is_email(is_email)

        if fighting_mode_name in [FightingMode.LiveMessage, FightingMode.LiveSimulate, FightingMode.LiveReal]:
            self.send_email_method(SendEmailMode.GroupMode)
            self.sig_merge()  # 每当发送一次email，就将消息数据整合
            # 执行定时器任务
            waiting_time = 0
            while True:
                if self.is_end:
                    break
                time.sleep(conf.Fighting.live_period.value.total_seconds())
                is_valid = self.monitor(data_path, is_traverse=True)  # 获取更新的数据并驱动引擎运行一次。
                if not is_valid:
                    waiting_time += 1
                else:
                    waiting_time = 0
                    self.send_email_method(SendEmailMode.OneMode)
                    self.sig_merge()  # 每当发送一次email，就将消息数据整合

                if waiting_time * conf.Fighting.live_period.value > conf.Fighting.waiting_max_time.value:
                    # 如果连续等待时间超过了最大等待时间
                    info = "等待实盘数据更新超时。"
                    self.super_model.master.file_manager.log_engine.emit(info, LogName.Running)
                    raise ValueError("等待实盘数据更新超时。")
            pass
        elif fighting_mode_name in [FightingMode.HistoryMessage, FightingMode.HistorySimulate]:
            # self.monitor(data_path, is_traverse=True)
            self.send_email_method(SendEmailMode.GroupMode)
            self.sig_merge()  # 每当发送一次email，就将消息数据整合
            pass
        else:
            pass

    def sig_merge(self):
        # 将信号整合到ori_data中并予以存储
        # 信号数据
        message_dc: dict[str, MessageData] = self.trade_server.message_dc
        ori_data_dc = self.data_server.ori_data_dc
        valid_mess_dc: dict[str, list] = {}
        for message_id, message in message_dc.items():
            if message.status in [Status.NOTTRADED]:
                symbol = message.symbol
                col_name = SigType.Buy.value if message.direction == Direction.LONG else \
                    SigType.Sell.value if message.direction == Direction.SHORT else \
                        SigType.Mess.value
                if symbol in valid_mess_dc.keys():
                    valid_mess_dc[symbol].append([message.datetime, col_name, message.price])
                else:
                    valid_mess_dc[symbol] = [[message.datetime, col_name, message.price]]
                message.status = Status.ALLTRADED
        # 将提取的数据放入ori data中
        for symbol, mess_ls in valid_mess_dc.items():
            for mess in mess_ls:
                if mess[1] not in ori_data_dc[symbol].columns:
                    ori_data_dc[symbol][mess[1]] = np.nan
                ori_data_dc[symbol].at[mess[0], mess[1]] = mess[2]
        # 存储整合后的数据到指定文件夹内
        fighting_mode = self.super_model.fighting_mode
        if fighting_mode in [FightingMode.HistoryMessage.value, FightingMode.HistorySimulate.value]:
            time_str = conf.FileManager.f.value.join(
                self.super_model.result_folder.split(conf.FileManager.f.value)[-2:])
            data_name = gen_data_name(DataCategory.MkFtSg, time_str=time_str)
            data_path = os.path.join(self.super_model.master.file_manager.market_factor_signal_data_path, f"{data_name}.csv")
            # 将信号数据保存到文件。
            self.super_model.master.file_manager.save_dc_csv(ori_data_dc, path=data_path)
        elif fighting_mode in [FightingMode.LiveMessage.value, FightingMode.LiveSimulate.value, FightingMode.LiveReal.value]:
            # time_str = conf.FileManager.f.value.join(
            #     self.super_model.result_folder.split(conf.FileManager.f.value)[-2:])
            # data_name = gen_data_name(DataCategory.MkFtSgLive, time_str=time_str)
            # data_path = os.path.join(self.super_model.master.file_manager.market_factor_signal_live_data_path, f"{data_name}.csv")
            pass
        else:
            raise ValueError("fighting模式错误，逻辑错误")

    def set_is_end(self, is_end):
        self.data_server.is_end = is_end

    def set_is_email(self, is_email):
        self.trade_server.set_is_email(is_email)

    def node(self, node_dc: dict[str, BarData]):
        # data_server回调
        # 执行trade server中的依靠node相关方法
        self.trade_server.update_node_dc(node_dc)
        self.trade_server.update_position_by_node(node_dc)
        self.trade_server.update_account_by_node(node_dc)
        self.trade_server.update_orders_by_node(node_dc)
        # 更新template中的仓位及账户
        self.template.update_pos_acc(self.trade_server.position_dc,
                                     self.trade_server.account)
        # 调用template的notify_cashvalue回调函数
        cash = self.trade_server.account.balance
        value = self.trade_server.account.balance + self.trade_server.account.frozen
        self.template.notify_cashvalue(cash, value)

        # 更新消息
        self.trade_server.update_messages_by_node(node_dc)
        # node回调
        self.template.get_node_dc(node_dc)
        pass

    def base_bar(self, base_bar: BarData):
        # data_server回调
        self.template.get_bar(base_bar)

    def start(self):
        self.template.trade = True

        self.template.start()

    def stop(self):
        # 人工介入或数据遍历完毕的结束逻辑。
        # 整合交易信号数据到初始

        # 策略模板执行策略层面的结束操作
        self.template.trade = False
        self.template.stop()
        self.is_end = True
        pass

    def send_email_method(self, send_email_mode: SendEmailMode):
        # 处理历史数据遍历后
        # 判断是否为实时数据状态，并执行发送邮件的逻辑
        if self.trade_server.is_email:
            self.trade_server.send_email(send_email_mode)
        else:
            pass

    def monitor(self, data_path, is_traverse):
        status = self.data_server.monitor(data_path, self.node, self.base_bar, is_traverse)
        return status
